/**
 * common.c - common operations
 * date:    2012-2-13 8:42:56
 * author:  Aaron<leafy.myeh@allwinnertech.com>
 * history: V0.1
 */
#include "pm_i.h"
static __s32 print_align(char *string, __s32 len, __s32 align);

#define NUM_TYPE long long

#define ZEROPAD	1		/* pad with zero */
#define SIGN	2		/* unsigned/signed long */
#define PLUS	4		/* show plus */
#define SPACE	8		/* space if plus */
#define LEFT	16		/* left justified */
#define SMALL	32		/* Must be 32 == 0x20 */
#define SPECIAL	64		/* 0x */
#define is_digit(c)	((c) >= '0' && (c) <= '9')


/* Basic string functions */

/*
  s t r l e n

  returns number of characters in s (not including terminating null character)
*/
static size_t strlen(const char *s)
{
	const char *sc;

	for (sc = s; *sc != '\0'; ++sc)
	{
		/* nothing */
		;
	}
	return sc - s;
}

/*
  s t r c p y

  Copy 'src' to 'dest'. Strings may not overlap.
*/
static char *strcpy(char *dest, const char *src)
{
	char *tmp = dest;

	while ((*dest++ = *src++) != '\0')
	{
		/* nothing */
		;
	}
	return tmp;
}

static char *strncpy(char *dest, const char *src, size_t count)
{
	char *tmp = dest;

	while (count)
	{
		if ((*tmp = *src) != 0)
		{
			src++;
		}
		tmp++;
		count--;
	}
	return dest;
}


static char *strcat(char *dest, const char *src)
{
	char *tmp = dest;

	while (*dest)
	{
		dest++;
	}
	while ((*dest++ = *src++) != '\0')
	{
		;
	}
	return tmp;
}


static char *strncat(char *dest, const char *src, size_t count)
{
	char *tmp = dest;

	if (count)
	{
		while (*dest)
		{
			dest++;
		}
		while ((*dest++ = *src++) != 0)
		{
			if (--count == 0)
			{
				*dest = '\0';
				break;
			}
		}
	}
	return tmp;
}

static int strcmp(const char *cs, const char *ct)
{
	unsigned char c1, c2;

	while (1)
	{
		c1 = *cs++;
		c2 = *ct++;
		if (c1 != c2)
		{
			return c1 < c2 ? -1 : 1;
		}
		if (!c1)
		{
			break;
		}
	}
	return 0;
}


static int strncmp(const char *cs, const char *ct, size_t count)
{
	unsigned char c1, c2;

	while (count) {
		c1 = *cs++;
		c2 = *ct++;
		if (c1 != c2)
			return c1 < c2 ? -1 : 1;
		if (!c1)
			break;
		count--;
	}
	return 0;
}

static int skip_atoi(const char **s)
{
	int i=0;

	while (is_digit(**s))
		i = i*10 + *((*s)++) - '0';
	return i;
}

static char *itoa(int value, char *string, int radix)
{
	char stack[16];
	int  negative = 0;			//defualt is positive value
	int  i;
	int  j;
	char digit_string[] = "0123456789ABCDEF";	
	
	if(value == 0)
	{
		//zero
		string[0] = '0';
		string[1] = '\0';
		return string;
	}
	
	if(value < 0)
	{
		//'value' is negative, convert to postive first
		negative = 1;
		value = -value ;
	}
	
	for(i = 0; value > 0; ++i)
	{
		// characters in reverse order are put in 'stack'.
		stack[i] = digit_string[value % radix];
		value /= radix;
	}
	
	//restore reversed order result to user string
    j = 0;
	if(negative)
	{
		//add sign at first charset.
		string[j++] = '-';
	}
	for(--i; i >= 0; --i, ++j)
	{
		string[j] = stack[i];
	}
	//must end with '\0'.
	string[j] = '\0';
	
	return string;
}

static char *utoa(unsigned int value, char *string, int radix)
{
	char stack[16];
	int  i;
	int  j;
	char digit_string[] = "0123456789ABCDEF";	
	
	if(value == 0)
	{
		//zero
		string[0] = '0';
		string[1] = '\0';
		return string;
	}
	
	for(i = 0; value > 0; ++i)
	{
		// characters in reverse order are put in 'stack'.
		stack[i] = digit_string[value % radix];
		value /= radix;
	}
	
	//restore reversed order result to user string
    for(--i, j = 0; i >= 0; --i, ++j)
	{
		string[j] = stack[i];
	}
	//must end with '\0'.
	string[j] = '\0';
	
	return string;
}

/*
*********************************************************************************************************
*                                       	FORMATTED PRINTF
*
* Description: 	print out a formatted string, similar to ANSI-C function printf().
*				This function can support and only support the following conversion specifiers:
*              	%d	signed decimal integer.
*              	%u	unsigned decimal integer.
*              	%x	unsigned hexadecimal integer, using hex digits 0x.
*              	%c	single character.
*              	%s	character string.
*
* Arguments  : 	format	: format control.
*				...		: arguments.
*
* Returns    : 	the number of characters printed out.
*
* Note		 : 	the usage refer to ANSI-C function printf().
*********************************************************************************************************
*/
static char debugger_buffer[DEBUG_BUFFER_SIZE];
__s32 printk(const char *format, ...)
{
	va_list args;
	char 	string[16];	//align by cpu word
	char 	*pdest;
	char 	*psrc;
	__s32 	align;
	__s32		len = 0;
	
	//dump current timestemp
	//print_current_time();
	
	pdest = debugger_buffer;
	va_start(args, format);
	while(*format)
	{
		if(*format == '%')
		{
			++format;
			if (('0' < (*format)) && ((*format) <= '9'))
			{
				//we just suport wide from 1 to 9.
				align = *format - '0';
				++format;
			}
			else
			{
				align = 0;
			}
			switch(*format)
			{
				case 'd':
				{
					//int
					itoa(va_arg(args, int), string, 10);
                    len = strlen(string);
                    len += print_align(string, len, align);
                    strcpy(pdest, string);
                    pdest += len;
                    break;
				}
				case 'x': 
				case 'p':
				{
					//hex
					utoa(va_arg(args, int), string, 16);
					len = strlen(string);
					len += print_align(string, len, align);
					strcpy(pdest, string);
                    pdest += len;
                    break;
				}
				case 'u': 
				{
					//unsigned int
					utoa(va_arg(args, int), string, 10);
                    len = strlen(string);
                    len += print_align(string, len, align);
                    strcpy(pdest, string);
					pdest += len;
					break;
				}
				case 'c': 
				{
					//charset, aligned by cpu word
					*pdest = (char)va_arg(args, int);
					break;
				}
				case 's':
				{
					//string
					psrc = va_arg(args, char *);
					strcpy(pdest, psrc);
					pdest += strlen(psrc);
					break;
				}
				default : 
				{
					//no-conversion
					*pdest++ = '%';
					*pdest++ = *format;
				}
			}
		}
		else
		{
			*pdest++ = *format;
		}
		//parse next token
		++format;
	}
	va_end(args);
	
	//must end with '\0'
	*pdest = '\0';
	pdest++;
	serial_puts(debugger_buffer);
	
	return (pdest - debugger_buffer);
}


static __s32 print_align(char *string, __s32 len, __s32 align)
{
	//fill with space ' ' when align request,
	//the max align length is 16 byte.
	char fill_ch[] = "                ";
	if (len < align)
	{
		//fill at right
		strncat(string, fill_ch, align - len);
		return align - len;
	}
	//not fill anything
	return 0;
}

__s32 printk_nommu(const char *format, ...)
{
	va_list args;
	char 	string[16];	//align by cpu word
	char 	*pdest;
	char 	*psrc;
	__s32 	align;
	__s32		len = 0;
	
	//dump current timestemp
	//print_current_time();
	
	pdest = debugger_buffer;
	va_start(args, format);
	while(*format)
	{
		if(*format == '%')
		{
			++format;
			if (('0' < (*format)) && ((*format) <= '9'))
			{
				//we just suport wide from 1 to 9.
				align = *format - '0';
				++format;
			}
			else
			{
				align = 0;
			}
			switch(*format)
			{
				case 'd':
				{
					//int
					itoa(va_arg(args, int), string, 10);
                    len = strlen(string);
                    len += print_align(string, len, align);
                    strcpy(pdest, string);
                    pdest += len;
                    break;
				}
				case 'x': 
				case 'p':
				{
					//hex
					utoa(va_arg(args, int), string, 16);
					len = strlen(string);
					len += print_align(string, len, align);
					strcpy(pdest, string);
                    pdest += len;
                    break;
				}
				case 'u': 
				{
					//unsigned int
					utoa(va_arg(args, int), string, 10);
                    len = strlen(string);
                    len += print_align(string, len, align);
                    strcpy(pdest, string);
					pdest += len;
					break;
				}
				case 'c': 
				{
					//charset, aligned by cpu word
					*pdest = (char)va_arg(args, int);
					break;
				}
				case 's':
				{
					//string
					psrc = va_arg(args, char *);
					strcpy(pdest, psrc);
					pdest += strlen(psrc);
					break;
				}
				default : 
				{
					//no-conversion
					*pdest++ = '%';
					*pdest++ = *format;
				}
			}
		}
		else
		{
			*pdest++ = *format;
		}
		//parse next token
		++format;
	}
	va_end(args);
	
	//must end with '\0'
	*pdest = '\0';
	pdest++;
	serial_puts_nommu(debugger_buffer);
	
	return (pdest - debugger_buffer);
}

